package com.lalameow.researchstation.api.technology

import com.lalameow.researchstation.ResearchStation
import com.lalameow.researchstation.identifier
import com.lalameow.researchstation.kube.Builder
import com.lalameow.researchstation.kube.TechnologyCriteria
import com.lalameow.researchstation.kube.TechnologyRequirements
import com.lalameow.researchstation.kube.TechnologyResources
import com.lalameow.researchstation.kube.TechnologyRewards
import com.mojang.serialization.Lifecycle
import net.minecraft.util.Identifier
import net.minecraft.util.registry.MutableRegistry
import net.minecraft.util.registry.Registry
import net.minecraft.util.registry.RegistryKey
import net.minecraft.util.registry.SimpleRegistry

object TechnologyRegistries {
    private val ROOT: MutableRegistry<MutableRegistry<*>> =
        SimpleRegistry(key("root"), Lifecycle.experimental(), null)
    val REGISTRIES: Registry<out Registry<*>> = ROOT

    val TECHNOLOGY_RESOURCE_KEY = key<() -> Builder<out TechnologyResource<*>>>("resource")
    val TECHNOLOGY_REQUIREMENT_KEY =
        key<() -> Builder<out TechnologyRequirement<*>>>("requirement")
    val TECHNOLOGY_REWARD_KEY = key<() -> Builder<out TechnologyReward>>("reward")
    val TECHNOLOGY_CRITERION_KEY = key<() -> Builder<out TechnologyCriterion>>("criterion")

    val TECHNOLOGY_RESOURCE = create(TECHNOLOGY_RESOURCE_KEY)
    val TECHNOLOGY_REQUIREMENT = create(TECHNOLOGY_REQUIREMENT_KEY)
    val TECHNOLOGY_REWARD = create(TECHNOLOGY_REWARD_KEY)
    val TECHNOLOGY_CRITERION = create(TECHNOLOGY_CRITERION_KEY)

    init {
        requireNotNull(TechnologyResources.ITEM)
        requireNotNull(TechnologyRewards.ITEM)
        requireNotNull(TechnologyCriteria.TECHNOLOGY)
        requireNotNull(TechnologyRequirements.ITEM)
    }

    private fun <T> key(id: String): RegistryKey<Registry<T>> {
        return RegistryKey.ofRegistry(ResearchStation.identifier(id))
    }

    private fun <T> create(key: RegistryKey<out Registry<T>>): Registry<T> {
        return create(key, Lifecycle.experimental())
    }

    private fun <S, T : Registry<S>> create(key: RegistryKey<T>, lifecycle: Lifecycle): T {
        return create(
            key,
            SimpleRegistry(key, lifecycle, null),
            lifecycle
        ) as T
    }

    private fun <R, S : MutableRegistry<R>, T : Registry<R>> create(
        key: RegistryKey<T>,
        registry: S,
        lifecycle: Lifecycle
    ): S {
        ROOT.add(key as RegistryKey<MutableRegistry<*>>, registry, lifecycle)
        return registry
    }
}

object Technologies {
    private val technologies: MutableMap<Identifier?, Technology> = HashMap()

    operator fun plusAssign(technology: Technology) = run { technologies[technology.id] = technology }

    operator fun get(id: Identifier) = technologies[id]

    fun all() = technologies.values.toList()

    fun clear() = technologies.clear()
}

object TechnologyCategories {
    private val categories: MutableMap<Identifier?, TechnologyCategory> = HashMap()

    operator fun plusAssign(category: TechnologyCategory) = run { categories[category.id] = category }

    operator fun get(id: Identifier) = categories[id]

    fun all() = categories.values.toList()

    fun clear() = apply {
        categories.clear()
        Technologies.clear()
    }
}
